﻿#pragma once

#include <matsu.h>
#include "SRGLinc.h"
#include "SRType.h"

namespace sora
{
	class Vertex2;
	class Vertex3;
	class VertexPointerBuilder;
	class VertexPointer;
	class DrawCommand;

	class Vertex2 {
	public:
		float pos[2];
		float color[4];
	};

	class Vertex3 {
	public:
		Vertex3(const matsu::vec3 &pos, const matsu::vec4 &color, const matsu::vec2 &texCoord, const matsu::vec3 &normal);
		Vertex3(const matsu::vec3 &pos, const matsu::vec4 &color, const matsu::vec2 &texCoord);
		Vertex3(const matsu::vec3 &pos, const matsu::vec4 &color);
		Vertex3(const matsu::vec3 &pos);

		float pos[3];
		float color[4];
		float texCoord[2];
		float normal[3];
	};

	class ModelVertex3 {
	public:
		ModelVertex3(const matsu::vec3 &pos, const matsu::vec2 &texCoord, const matsu::vec3 &normal);
		ModelVertex3(const matsu::vec3 &pos, const matsu::vec2 &texCoord);
		ModelVertex3(const matsu::vec3 &pos, const matsu::vec3 &normal);
		ModelVertex3(const matsu::vec3 &pos);

		float pos[3];
		float texCoord[2];
		float normal[3];
	};

	class ColorVertex3 {
	public:
		ColorVertex3(const matsu::vec3 &pos, const matsu::vec4 &color);
		
		float pos[3];
		float color[4];
	};

	class SimpleVertex3 {
	public:
		SimpleVertex3(const matsu::vec3 &pos);
		SimpleVertex3(const float *ptr);

		float pos[3];
	};

	class VertexPointerBuilder {
	public:
		VertexPointerBuilder();

		void reset();
		void build(VertexPointer &pointer);
		VertexPointer build();

		void setStartPointer(const GLvoid *start);
		void setVertexOffset(int offset);
		void setColorOffset(int offset);
		void setTexCoordOffset(int offset);
		void setNormalOffset(int offset);
		void setStride(GLsizei stride);
		void setCount(int count);

		template<typename T>
		int calcOffset(const T* p1, const T *p2) const { return calcOffset((void*)p1, (void*)p2); }
		int calcOffset(void* p1, void *p2) const;
	private:
		VertexPointerBuilder(const VertexPointerBuilder &other);
		VertexPointerBuilder& operator=(const VertexPointerBuilder &other);

		GLvoid *start_;
		int vertexOffset_;
		int colorOffset_;
		int texCoordOffset_;
		int normalOffset_;
		GLsizei stride_;
		int count_;
	};
	class VertexPointer {
	public:
		VertexPointer();
		const GLvoid *vertex() const;
		const GLvoid *color() const;
		const GLvoid *texCoord() const;
		const GLvoid *normal() const;
		GLsizei stride() const;
		int count() const;
		const GLvoid *start() const;

		GLvoid* vertexOffset() const;
		GLvoid* colorOffset() const;
		GLvoid* texCoordOffset() const;
		GLvoid* normalOffset() const;

		bool useVertexPointer() const;
		bool useColorPointer() const;
		bool useTexCoordPointer() const;
		bool useNormalPointer() const;

	private:
		GLvoid *start_;
		int vertexOffset_;
		int colorOffset_;
		int texCoordOffset_;
		int normalOffset_;
		GLsizei stride_;
		int count_;

		friend class VertexPointerBuilder;
	};

	class DrawCommand {
	public:
		DrawCommand(GLenum mode, GLsizei count, GLenum type, const GLvoid *indices, const VertexPointer &pointer);
		GLenum mode() const;
		GLsizei count() const;
		GLenum type() const;
		const GLvoid *indices() const;
		const VertexPointer &pointer() const;

	private:
		GLenum mode_;
		GLsizei count_;
		GLenum type_;
		const GLvoid *indices_;
		VertexPointer pointer_;
	};

	class Box {
	public:
		Box(float top, float bottom, float left, float right, float front, float back);
		Box(const std::vector<SimpleVertex3> &vertexList);
		Box(const std::vector<ModelVertex3> &vertexList);
		Box(const std::vector<ColorVertex3> &vertexList);
		Box(const std::vector<Vertex3> &vertexList);

		Box& operator+=(const Box &o);
		Box operator+(const Box &o);

		float top() const;
		float bottom() const;
		float left() const;
		float right() const;
		float front() const;
		float back() const;

		float width() const;
		float height() const;
		float depth() const;

	private:
		float bottom_;
		float top_;
		float left_;
		float right_;
		float front_;
		float back_;

		void processVertex(float x, float y, float z);
		void setInvalidValue();
	};

	class Color {
	public:
		static color4 red();
		static color4 green();
		static color4 blue();
		static color4 white();
		static color4 black();
	};
}

